from __future__ import absolute_import

import logging
import traceback
from enum import Enum
from collections import namedtuple
from datetime import datetime, timedelta

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

from ae_trace.middleware import get_template
from ae_trace.apps import TraceConfig
from ae_trace.models import AlarmData
from ae_trace.serializer import AlarmTraceDataSerializer
from ae_trace.documents import AlarmTraceDocument as adoc

from common.ploto_enum import CommonStatuEnum, DataStatuEnum
from common.ploto_response import PlotoResponse

from .utils import (es_search, db_search, get_union_field_values,
                    check_body, set_trace_view, get_page_list,
                    response_total)


logger = logging.getLogger(__name__)


class AlarmStatus(Enum):
    unhandled = 1
    handled = 2
    mark = 3
    ignore = 4


class AlarmTraceData(APIView):

    def __init__(self):
        self.template = get_template(TraceConfig.template_alarm)
        set_trace_view(self)
        # 将结果倒序排列
        self.order = {
            "es": {
                "last_operate_time": {"order": "desc"}
            },
            "db": "-last_operate_time"
        }
        self.AlarmParam = namedtuple(
            'AlarmParam', ['obj_list', 'change_state', 'success_object', 'fail_dict'])


    def get(self, request):
        response = PlotoResponse()
        if not request.user.has_perm('ae_trace.view_alarmdata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)

        body = request.query_params
        if body.get("search_max"):
            self.search_max = int(body.get("search_max"))
        union_field_values = request.session.get("union_field_values_alarm")
        if body.get('reset') or not union_field_values:
            union_field_values = self.reset_search()
            request.session["union_field_values_alarm"] = union_field_values
        page = int(body.get('page', 1))
        page_size = int(body.get('page_size', 10))

        if self.search_engine == "es":
            time_field = {"last_operate_time": ["%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S.%f"]}
            body = check_body(body, time_field=time_field, fields=["alarm_source"])
            search_res_list = es_search(doc=adoc, order=self.order["es"],
                search_method=self.es_search_method, search_max=self.search_max,
                time_field=["last_operate_time"], **body)
            if search_res_list.total.value == 0:
                return response_total(response, data_list=[], count=0,
                                    union_field_values=union_field_values)

        elif self.search_engine == "db":
            time_field = {"last_operate_time": ["%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M:%S.%f"]}
            body = check_body(body, time_field=time_field, fields=["alarm_source"])
            search_res_list = db_search(db_table=AlarmData, order=self.order["db"],
                search_method=self.db_search_method, search_max=self.search_max, **body)
            if search_res_list.count() == 0:
                return response_total(response, data_list=[], count=0,
                                    union_field_values=union_field_values)
        page_search_res_list, paginator = get_page_list(search_res_list, page, page_size)
        try:
            search_res = AlarmTraceDataSerializer(instance=page_search_res_list, many=True)
            return response_total(response, data_list=search_res.data,
                                count=paginator.count,
                                union_field_values=union_field_values)
        except Exception as e:
            logger.error("序列化异常: %s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def put(self, request):
        """
        批量修改告警处理状态
        param: request
        return: data
        1、判断请求id的合法性
        2、判断变更操作是否遵从变更规则
        已处理、屏蔽可恢复为未处理，线下处理可变更为屏蔽/未处理/已处理
        """
        response = PlotoResponse()
        if not request.user.has_perm('ae_trace.change_alarmdata'):
            response.code = CommonStatuEnum.AUTHORIZATION_EXCEPTION.code
            response.msg = CommonStatuEnum.AUTHORIZATION_EXCEPTION.msg
            return Response(response.dict, status=status.HTTP_403_FORBIDDEN)

        params = request.data
        id_list = params.get('ids')
        change_state = params.get('state')
        if not id_list or not change_state:
            response.msg = CommonStatuEnum.VALID_EXCEPTION.msg
            response.code = CommonStatuEnum.VALID_EXCEPTION.code
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)

        state_list = (AlarmStatus.unhandled.value, AlarmStatus.ignore.value,
                       AlarmStatus.mark.value, AlarmStatus.handled.value)
        change_state = int(change_state)
        change_list = id_list.split(',')
        if change_state not in state_list:
            response.msg = DataStatuEnum.ALARM_STATUS_EXCEPTION.msg
            response.code = DataStatuEnum.ALARM_STATUS_EXCEPTION.code
            return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)

        obj_list = AlarmData.objects.filter(id__in=change_list)
        success_object = []
        fail_dict = {}
        alarm_param = self.AlarmParam(obj_list=obj_list, change_state=change_state,
                                      success_object=success_object, fail_dict=fail_dict)
        self._do_change(alarm_param)
        if success_object and not fail_dict:
            union_field_values = self.reset_search()
            request.session["union_field_values_alarm"] = union_field_values
            response.data["union_field_values"] = union_field_values
            return Response(response.dict, status=status.HTTP_200_OK)

        response.code = DataStatuEnum.ALARM_CHANGE_EXCEPTION.code
        response.msg = DataStatuEnum.ALARM_CHANGE_EXCEPTION.msg
        response.data = fail_dict
        return Response(response.dict, status=status.HTTP_400_BAD_REQUEST)


    def _do_change(self, alarm_param):
        obj_list, change_state, success_object, fail_dict = alarm_param
        for obj in obj_list:
            if int(obj.state) == AlarmStatus.ignore.value:
                if change_state != AlarmStatus.unhandled.value:
                    fail_dict["ignore state"] = "屏蔽状态只能转为未处理状态"
                    return
            if int(obj.state) == AlarmStatus.handled.value:
                if change_state != AlarmStatus.unhandled.value:
                    fail_dict["handled state"] = "已处理状态只能转为未处理状态"
                    return
            record = AlarmData.objects.get(id=obj.id)
            record.state = change_state
            record.save()
            success_object.append(obj.id)


    def reset_search(self):
        response = PlotoResponse()
        time_format = "%Y-%m-%dT%H:%M:%S.%f" if self.search_engine == "es" else "%Y-%m-%d %H:%M:%S.%f"
        start_time = datetime.strftime(datetime.today()-timedelta(days=180), time_format)
        end_time = datetime.strftime(datetime.today(), time_format)
        reset_body = {"last_operate_time": [start_time, end_time]}
        try:
            if self.search_engine == "es":
                search_res_list = es_search(doc=adoc, order=self.order["es"],
                    search_method=self.es_search_method,
                    search_max=self.search_max,
                    time_field=["last_operate_time"], **reset_body)
            elif self.search_engine == "db":
                search_res_list = db_search(db_table=AlarmData, order=self.order["db"],
                    search_method=self.db_search_method,
                    search_max=self.search_max, **reset_body)

            return get_union_field_values(search_res_list, self.search_engine, self.union_fields)

        except Exception as e:
            logger.error("reset search failed: %s, %s", repr(e), traceback.format_exc())
            response.code = CommonStatuEnum.INTERNAL_ERROR.code
            response.msg = CommonStatuEnum.INTERNAL_ERROR.msg
            return Response(response.dict, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
